<?php

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Filesystem\Filesystem;

class AppCarCommand extends Command {
	protected $name = 'app:car';
	public function makeFormElements($action, $fields) {
		$formMethods = array();
		foreach( $fields as $field ) {
			if(isset($field ['system'])) continue;
			$name = $field ['name'];
			$type = $field ['type'];
			$formalName = ucwords( $name );
			$options = array('class' => '','placeholder' => $formalName,'title' => $formalName );
			// TODO: add remaining types
			if('edit' == $action) {
				$value = "{{ Input::old('$name', \$".$this->replaces ['{{modelInstance}}']."->$name) }}";
			} else {
				$value = "{{ Input::old('$name') }}";
			}

			switch($type) {
				case 'integer' :
					// $element = "{{ Form::input('number', '$name') }}";
					$element = Form::input('number', $name, $value, $options );
					break;
				case 'text' :
					// $element = "{{ Form::textarea('$name') }}";
					$element = Form::textarea($name, $value, array_merge($options, array('rows' => 2,'cols' => 100 )));
					break;
				case 'boolean' :
					// $element = "{{ Form::checkbox('$name') }}";
					$element = Form::checkbox( $name, 1, "{{ Input::old('$name') == 1}}" );
					break;
				default :
					// $element = "{{ Form::text('$name') }}";
					$element = Form::text( $name, $value, $options );
					break;
			}

			$element = $this->replace( $element );

			$frag = <<<EOT
<div  class="control-group {{ \$errors->first('$name', ' error') }}">
	<label class="control-label" for="$name">$formalName</label>
	<div class="controls">
	$element
	{{ \$errors->first('$name', '<span class="help-inline">:message</span>') }}
	</div>
</div>
EOT;
			$formMethods [] = $frag;
		}
		return implode( PHP_EOL, $formMethods );
	}

    protected function getArguments() {
        return array(
            array('domain',InputArgument::REQUIRED,'An domain argument.'),
            array('entity',InputArgument::REQUIRED,'An entity argument.')
        );
    }
    protected function getOptions() {
        return array(
            array('namespace',null,InputOption::VALUE_OPTIONAL,'An namespace option.','nimo.domains'),
            array('template',null,InputOption::VALUE_OPTIONAL,'An template path  option.',app_path().'/lib/stubs/car_balance/template'),
            array('path',null,InputOption::VALUE_OPTIONAL,'An source out path option.',null ),
            array('fields',null,InputOption::VALUE_OPTIONAL,'Table fields',null ) ,
            array('forced',null,InputOption::VALUE_OPTIONAL,'regenerate all', null ) ,
        );
    }

    /** @var  array */
    protected $skeletonDirs;
    /** @var  FileSystem */
    protected $fileSystem;

    public function setSkeletonDirs($skeletonDirs)
    {
        $this->skeletonDirs = is_array($skeletonDirs) ? $skeletonDirs : array($skeletonDirs);
    }

    public function fire()
    {
        $this->setSkeletonDirs(app_path().'/lib/stubs/car_balance/twig');

        $yamlParser = new \Symfony\Component\Yaml\Parser();
        $this->fileSystem = new FileSystem();
        $tables = $yamlParser->parse(file_get_contents(app_path().'/lib/stubs/car_balance/schema.yml'));
        $fixtures = $yamlParser->parse(file_get_contents(app_path().'/lib/stubs/car_balance/fixtures.yml'));

        $namespace = 'nimo\domains\car_balance';
        foreach($tables as $table => $schema) {
            $table_name = $schema['table'];
            $modelClass = $this->getCamelCaseName($table);
            $fixture = isset($fixtures[$table]) ? $fixtures[$table] : array();

            // Generate migration
            $migration = $this->generateMigration($table_name, $schema, 'nimo\database\migrations', $modelClass);

            // Generate seeds
            $seed = $this->generateSeed($table_name, $schema, $fixture, 'nimo\database\seeds', $modelClass);

            // Generate Model class
            $model = $this->generateModel($table_name, $schema, $namespace, $modelClass);

            // Generate Controller class
            $controller = $this->generateController($table, $schema, $namespace, $modelClass);

            // Generate views
            foreach(array('index', 'create', 'edit', 'show') as $action) {
                $this->generateViews($action, $table, $modelClass, $schema, $namespace);
            }
            // TODO: Generate tests

            // Run migrate, seed command
            //$this->call('migrate', array('--path' => 'app/lib/src/nimo/database/migrations'));
            //$this->call('db:seed', array('--class' => 'nimo\\database\\seeds\\'.$modelClass.'Seeder'));
        }

        // Generate routing file
        $routes = $this->generateRoutes($tables, $namespace, 'car_balance');

        // Generate menus
        $menus = $this->generateMenus($tables, $namespace, 'car_balance');
        //var_dump($menus);
        die;
    }

    protected function render($template, $parameters)
    {
        $twig = new \Twig_Environment(new \Twig_Loader_Filesystem($this->skeletonDirs), array(
            'debug'             => true,
            'cache'             => false,
            'strict_variables'  => true,
            'autoescape'        => false,
        ));

        return $twig->render($template, $parameters);
    }

    protected function generate($table_name, $schema)
    {

    }


    /**
     * Generate Controller class
     *
     * @param string $table model_name
     * @param array $schema schema
     * @param string $namespace
     * @param string $modelClass
     * @param bool $dumpFile
     * @return string generated Controller class content
     */
    protected function generateController($table, $schema, $namespace = null, $modelClass = null, $dumpFile = true)
    {
        $modelClass = $modelClass ? $modelClass : $this->getCamelCaseName($table);
        $namespace = $namespace ? $namespace : 'nimo\domains\car_balance';
        $columns = $schema['columns'];
        $actAs = $schema['actAs'];
        $fields = array();
        $validationRules = array();
        $relations = array();
        foreach($columns as $column_name => $definition) {
            // skip autoincrement id
            if(isset($definition['autoincrement']))
                continue;
            // form field
            $fields[$column_name] = $column_name;
            // validation rule
            $validationRules[$column_name] = $this->guessRule($definition);
        }

        if(isset($schema['relations']))
            foreach($schema['relations'] as $relationType => $relation ) {
                foreach($relation as $relation_name => $related_model) {
                    $relations[$relationType][$related_model] = $this->getCamelCaseName($related_model);
                }
            }


        $template = $this->render('controller.twig', array(
            'namespace'     => $namespace,
            'model_name'    => $table,
            'model_class'   => $modelClass,
            'relations'     => $relations,
            'softdelete'    => array_key_exists('softDeleteable', $actAs) ? (is_null($actAs['softDeleteable']) ? true : $actAs['softDeleteable']) : false,
            'validationRules' => $validationRules,
            'fields'        => $fields,
            'route_prefix'  => 'car_balance'
        ));

        if($dumpFile === true) {
            $this->dumpFile($namespace.'/Controller/'.$modelClass.'Controller.php', $template);
        }
        return $template;
    }

    /**
     * Generate Model class
     *
     * @param string $table_name
     * @param array  $schema
     * @param string $namespace
     * @param string $modelClass
     * @param bool   $dumpFile
     * @return string generated Model class content
     */
    protected function generateModel($table_name, $schema, $namespace, $modelClass, $dumpFile = true)
    {
        $actAs = $schema['actAs'];
        $relations = array();

        if(isset($schema['relations']))
            foreach($schema['relations'] as $relationType => $relation ) {
                foreach($relation as $relation_name => $related_model) {
                    $relations[$relationType][$relation_name] = $this->getCamelCaseName($related_model);
                }
            }

        $template = $this->render('model.twig', array(
            'namespace'     => $namespace,
            'table_name'    => $table_name,
            'model_class'   => $modelClass,
            'softdelete'    => array_key_exists('softDeleteable', $actAs) ? (is_null($actAs['softDeleteable']) ? true : $actAs['softDeleteable']) : false,
            'timestamps'    => array_key_exists('timestampable', $actAs) ? (is_null($actAs['timestampable']) ? true : $actAs['timestampable']) : false,
            'relations'     => $relations,
        ));

        if($dumpFile === true) {
            $this->dumpFile($namespace.'/Model/'.$modelClass.'.php', $template);
        }
        return $template;
    }

    /**
     * Generate migration class
     *
     * @param $table_name
     * @param $schema
     * @param $namespace
     * @param $modelClass
     * @param bool $dumpFile
     * @return string generated migration class content
     */
    protected function generateMigration($table_name, $schema, $namespace, $modelClass, $dumpFile = true)
    {
        $columns = $schema['columns'];
        $actAs = $schema['actAs'];

        $template = $this->render('migration.twig', array(
            'model_class'   => $modelClass,
            'softdelete'    => array_key_exists('softDeleteable', $actAs) ? (is_null($actAs['softDeleteable']) ? true : $actAs['softDeleteable']) : false,
            'timestamps'    => array_key_exists('timestampable', $actAs) ? (is_null($actAs['timestampable']) ? true : $actAs['timestampable']) : false,
            'columns'       => $columns,
            'table_name'    => $table_name,
            'class_name'    => $this->getCamelCaseName($table_name),
        ));

        if($dumpFile === true) {
            $dir = app_path().'/lib/src/'.str_replace('\\', DIRECTORY_SEPARATOR, $namespace);
            $new_file = 'create_'.$table_name.'_table.php';
            foreach(scandir($dir) as $file) {
                $old_file = implode('_', array_slice(explode('_', $file), 4));
                if($old_file == $new_file)
                    $this->fileSystem->remove($dir.DIRECTORY_SEPARATOR.$file);
            }
            $this->dumpFile($namespace.'/'.date('Y_m_d_His_').$new_file, $template);
        }
        return $template;
    }

    /**
     * Generate seeds class
     *
     * @param $table_name
     * @param $schema
     * @param $fixture
     * @param $namespace
     * @param $modelClass
     * @param bool $dumpFile
     * @return string Generated seeds class content
     */
    protected function generateSeed($table_name, $schema, $fixture, $namespace, $modelClass, $dumpFile = true)
    {
        $columns = $schema['columns'];
        $actAs = $schema['actAs'];

        $template = $this->render('seed.twig', array(
            'namespace'     => $namespace,
            'model_class'   => $modelClass,
            'columns'       => $columns,
            'fixture'       => isset($fixture['seeds']) ? $fixture['seeds'] : array(),
            'table_name'    => $table_name,
            'timestamps'    => array_key_exists('timestampable', $actAs) ? (is_null($actAs['timestampable']) ? true : $actAs['timestampable']) : false,
        ));

        if($dumpFile === true) {
            $this->dumpFile($namespace.'/'.$modelClass.'Seeder.php', $template);
        }
        return $template;
    }

    protected function generateViews($action, $table, $modelClass, $schema, $namespace, $dumpFile = true)
    {
        $columns = $schema['columns'];
        $actAs = $schema['actAs'];
        $relations = array();

        if(isset($schema['relations']))
            foreach($schema['relations'] as $relationType => $relation ) {
                foreach($relation as $relation_name => $related_model) {
                    $relations[$relationType][$relation_name] = $related_model;
                    if($relationType == 'belongsTo')
                        unset($columns[$related_model.'_id']);
                }
            }

        $template = $this->render("views/$action.blade.twig", array(
            'model_class'   => $modelClass,
            'model_name'    => $table,
            'fields'        => $columns,
            'relations'     => $relations,
            'route_prefix'  => 'car_balance',
            'softdelete'    => array_key_exists('softDeleteable', $actAs) ? (is_null($actAs['softDeleteable']) ? true : $actAs['softDeleteable']) : false,
            'timestamps'    => array_key_exists('timestampable', $actAs) ? (is_null($actAs['timestampable']) ? true : $actAs['timestampable']) : false,

            'base_layout'   =>'frontend/layouts/flaty',
            'section_title' =>'content.titley',
            'section_breadcrumb' =>'content.breadcrumb.inner',
            'section_content' =>'content.main',

        ));

        if($dumpFile === true) {
            $this->dumpFile('views/'.$namespace.'/'.$modelClass.'/'.$action.'.blade.php', $template);
        }
        return $template;
    }

    protected function generateRoutes($tables, $namespace, $route_prefix, $dumpFile = true)
    {
        $models = array();
        foreach($tables as $table => $schema) {
            $models[$table] = array(
                'model_class'   => $this->getCamelCaseName($table),
                'model_name'    => $table,
                'softdelete'    => array_key_exists('softDeleteable', $schema['actAs']) ? (is_null($schema['actAs']['softDeleteable']) ? true : $schema['actAs']['softDeleteable']) : false,
            );
        }
        $template = $this->render("routes.twig", array(
            'namespace'     => $namespace,
            'models'        => $models,
            'route_prefix'  => $route_prefix,
        ));

        if($dumpFile === true) {
            $this->dumpFile($namespace.'/'.'routes.php', $template);
        }
        return $template;
    }

    protected function generateMenus($tables, $namespace, $route_prefix, $dumpFile = true)
    {
        $models = array();
        foreach($tables as $table => $schema) {
            $models[$table] = array(
                'model_name'    => $table,
            );
        }
        $template = $this->render("menus.twig", array(
            'namespace'     => $namespace,
            'models'        => $models,
            'route_prefix'  => $route_prefix,
        ));

        if($dumpFile === true) {
            $this->dumpFile('views/'.$namespace.'/'.'menus.blade.php', $template);
        }
        return $template;
    }

    /**
     * Guess form rules from column config
     *
     * @param array $definition The column config options
     * @return string Rules
     */
    private function guessRule($definition)
    {
        // validation rule
        $rule = '';
        // guess required
        if( (isset($definition['primary']) && $definition['primary'] == true)
            || ($definition['type'] != 'boolean' && !(array_key_exists('nullable', $definition) && $definition['nullable'] == true))) {
            $rule .= 'required';
        }
        // guess type
        if(in_array($definition['type'], array('integer', 'bigInt', 'int', 'smallInt', 'tinyInt'))) {
            $rule .=  ($rule != '' ? '|' : '') .'integer';
        } elseif(in_array($definition['type'], array('float', 'double', 'number'))) {
            $rule .=  ($rule != '' ? '|' : '') .'number';
        } elseif(strpos($definition['type'], 'string') !== false || strpos($definition['type'], 'varchar') !== false) {
            $rule .= ($rule != '' ? '|' : '') .'max:'.preg_replace("/[^0-9]/","",$definition['type']);
        } elseif(strpos($definition['type'], 'date') !== false || strpos($definition['type'], 'time') !== false) {
            $rule .= ($rule != '' ? '|' : '') .'date';
        }
        // guess unique
        if(isset($definition['unique']) && $definition['unique'] == true)
        $rule .=  ($rule != '' ? '|' : '') .'unique';

        return $rule;
    }

    /**
     * Convert foo_bar_baz to FooBarBaz
     *
     * @param string $name source_name
     * @return string The CamelCasedName
     */
    private function getCamelCaseName($name)
    {
        return str_replace(' ', '',ucwords(str_replace('_', ' ', $name)));
    }

    /**
     * Write contents to file
     *
     * @param $filename
     * @param $content
     */
    private function dumpFile($filename, $content)
    {
        $this->fileSystem->dumpFile(
            app_path().'/lib/src/'.str_replace('\\', DIRECTORY_SEPARATOR, $filename), $content
        );
    }
}
